package com.kk.springboot_002.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {
    // 生成密钥
    private final SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

    /**
     * 生成token
     * @param userDetail
     * @return
     */
    public String generateToken(UserDetails userDetail) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetail.getUsername());
    }
    private String createToken(Map<String, Object> claims, String username) {
        return Jwts.builder()
                // 设置自定义的载荷信息
                .setClaims(claims)
                // 设置用户名
                .setSubject(username)
                // 设置签发时间
                .setIssuedAt(new Date(System.currentTimeMillis()))
                // 设置过期时间(10h)
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                // 签名
                .signWith(key, SignatureAlgorithm.HS256)
                // 将所有部分压缩成最终的 JWT 字符串
                .compact();
    }

    /**
     * 验证令牌
     * @param token
     * @param userDetails
     * @return
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    /**
     * 获取用户名
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    /**
     * 提取特定信息(过期时间)
     * @param token
     * @return
     */
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    /**
     * 提取特定信息(用户名)
     * @param token
     * @param claimsResolver
     * @return
     */
    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 解析令牌
     * @param token
     * @return
     */
    private Claims extractAllClaims(String token) {
        return Jwts.parserBuilder()
                // 设置密钥
                .setSigningKey(key)
                // 构建解析器
                .build()
                // 解析token，并将其封装在一个 Jws<Claims> 对象中
                .parseClaimsJws(token)
                // 从 Jws<Claims> 对象中提取出 Claims 对象
                .getBody();
    }
}
